1 module hip.bind.interpreters.lua;
2 
3 version(Have_bindbc_lua):
4 private enum valid = true;
5 private enum invalid = false;
6 
7 import std.traits:Unqual;
8 enum isUnsigned(T) = is(T == ubyte) || is(T == ushort) || is(T == uint) || (is(T == ulong));
9 enum isSigned(T) = is(T == byte) || is(T == short) || is(T == int) || (is(T == long));
10 enum isInteger(T) = isUnsigned!T || isSigned!T;
11 enum isFloating(T) = is(T == float) || is(T == double);
12 enum isNumber(T) = isInteger!T || isFloating!T;
13 enum isStructPointer(T) = is(typeof(*T.init) == struct);
14 
15 
16 import hip.bind.interpreters;
17 
18 
19 private enum LUA_TOP = -1;
20 import bindbc.lua;
21 
22 alias LuaVoid = InterpreterVoid;
23 
24 alias LuaFunction = extern(C) nothrow int function(lua_State* L);
25 
26 void luaPushVar(T)(lua_State* L, T arg)
27 {
28     static if(isUnsigned!T)
29         lua_pushunsigned(L, arg);
30     else static if(isSigned!T)
31         lua_pushinteger(L, arg);
32     else static if(isFloating!T)
33         lua_pushnumber(L, arg);
34     else static if(is(T == bool))
35         lua_pushboolean(L, cast(int)arg);
36     else static if(is(T == typeof(null)))
37         lua_pushnil(L);
38     else static if(is(T == string))
39         lua_pushlstring(L, arg.ptr, arg.length);
40     else static if(is(T == struct))
41     {
42         import core.stdc.string;
43         void* data = lua_newuserdata(L, arg.sizeof);
44         memcpy(data, &arg, arg.sizeof);
45     }
46     else //Push classes, pointers and interfaces
47         lua_pushlightuserdata(L, cast(void*)arg);
48 }
49 
50 
51 T luaGetFromIndex(T)(lua_State* L, int ind)
52 {
53     static if(isInteger!T)
54     {
55         assert(lua_isinteger(L, ind), "Tried to get a "~T.stringof~" from a not integer");
56         lua_Integer i = lua_tointeger(L, ind);
57         return cast(T)i;
58     }
59     else static if(isFloating!T)
60     {
61         assert(lua_isnumber(L, ind), "Tried to get a "~T.stringof~" from a not number");
62         lua_Number n = lua_tonumber(L, ind);
63         return cast(T)n;
64     }
65     else static if(is(T == string))
66     {
67         import core.stdc.string;
68         assert(lua_isstring(L, ind), "Tried to get a "~T.stringof~" from a not string");
69         auto luaStr = lua_tostring(L, ind);
70         char[] str = new char[](strlen(luaStr));
71         return cast(T)str;
72     }
73     else static if(isStructPointer!T)
74     {
75         assert(lua_isuserdata(L, ind), "Tried to get a "~T.stringof~" from a not userdata");
76         void* data = lua_touserdata(L, ind);
77         return cast(T)data;
78     }
79     else static if(is(T == struct))
80     {
81         import core.stdc.string;
82         assert(lua_isuserdata(L, ind), "Tried to get a "~T.stringof~" from a not userdata");
83         void* data = lua_touserdata(L, ind);
84         //Remove constness as it is copying
85         T ret;
86         import core.internal.traits;
87         memcpy(&ret, data, T.sizeof);
88         return ret;
89     }
90     else static if(is(T == class) || is(T == interface))
91     {
92         assert(lua_islightuserdata(L, ind), "Tried to get a "~T.stringof~" from a not light user data");
93         return cast(T)lua_topointer(L, ind);
94     }
95 }
96 
97 
98 InterpreterResult!T luaCallFunc(T, Args...)(lua_State* L, string funcName, Args args)
99 {
100     lua_getglobal(L, (funcName~'\0').ptr);
101     assert(lua_isfunction(L, LUA_TOP), "Variable "~funcName~" is not a function");
102     static foreach(a; args)
103         push(a);
104 
105     static if(is(T == class) || is(T == struct))
106         enum returnCount = cast(int)__traits(allMembers, T).length;
107     else static if(is(T == LuaVoid))
108         enum returnCount = cast(int)0;
109     else
110         enum returnCount = cast(int)1;
111     
112     if(lua_pcall(L, args.length, returnCount, 0) != LUA_OK)
113     {
114         static if(is(T == LuaVoid))
115             return InterpreterResult!T(invalid, LuaVoid(invalid));
116         else
117             return InterpreterResult!T(invalid, T.init);
118     }
119 
120 
121     static if(is(T == LuaVoid))
122         return InterpreterResult!T(valid, LuaVoid(valid));
123     else static if(is(T == struct))
124     {
125         T ret;
126         static foreach(i, m; __traits(allMembers, T))
127         {
128             __traits(getMember, ret, m) = getFromIndex!(typeof(__traits(getMember, T, m)))(-(returnCount - cast(int)i));
129         }
130 
131         return InterpreterResult!T(valid, ret);
132     }
133     else
134         return InterpreterResult!T(valid, getFromIndex!T(LUA_TOP));
135 }
136 
137 extern(C) int externLua(alias Func)(lua_State* L) nothrow
138 {
139     import std.traits:Parameters, ReturnType;
140     import std.meta:staticMap;
141     
142     staticMap!(Unqual,Parameters!Func) params;
143     int stackCounter = 0;
144     
145     foreach_reverse(ref param; params)
146     {
147         stackCounter--;
148         param = luaGetFromIndex!(typeof(param))(L, stackCounter);
149     }
150     
151     try
152     {
153         static if(is(ReturnType!Func == void))
154         {
155             Func(params);
156             return 0;
157         }
158         else
159         {
160             luaPushVar(L, Func(params));
161             return 1;
162         }
163     }
164     catch(Exception e)
165     {
166         luaPushVar(L, null);
167         try{luaL_error(L, ("A D function threw: "~e.toString~'\0').ptr);}
168         catch(Exception e){luaL_error(L, "D threw when stringifying exception");}
169         return 1;
170     }
171 }
172 
173 
174 class LuaInterpreter : IHipInterpreter
175 {
176     public lua_State* L;
177     private string currentFile;
178     private LuaFunction[string] funcList;
179 
180     public this()
181     {
182         L = luaL_newstate();
183         //Open standard libs
184         luaL_openlibs(L);
185     }
186 
187     public bool loadFile(string fileName)
188     {
189         currentFile = fileName;
190         if(luaL_dofile(L, (fileName~"\0").ptr) != LUA_OK)
191         {
192             luaL_error(L, "HipremeEngine Lua interpreter error: %s:\n", lua_tostring(L, -1));
193             return false;
194         }
195         return true;
196     }
197     pragma(inline) void checkLuaState()
198     {
199         assert(L != null,"No Lua State is loaded");
200     }
201     public void reload()
202     {
203         checkLuaState();
204         loadFile(currentFile);
205     }
206 
207     void push(T)(T arg)
208     {
209         checkLuaState();
210         luaPushVar(L, arg);
211     }
212 
213     private T getFromIndex(T)(int ind)
214     {
215         checkLuaState();
216         return luaGetFromIndex!T(L, ind);
217     }
218 
219     public InterpreterResult!T call(T, Args...)(string funcName, Args args)
220     {
221         checkLuaState();
222         return luaCallFunc!T(L, funcName, args);
223     }
224     public void expose(string name, InterpreterCFunction func)
225     {
226         expose(name, cast(LuaFunction)func);
227     }
228     public void expose(string name, LuaFunction func)
229     {
230         immutable(char)* fName;
231         fName = (name~'\0').ptr;
232         funcList[name] = func;
233 
234         checkLuaState();
235         lua_pushcfunction(L, func);
236         lua_setglobal(L, fName);
237 
238     }
239     public bool hasFunction(string funcName)
240     {
241         checkLuaState();
242         lua_getglobal(L, (funcName~'\0').ptr);
243         return lua_isfunction(L, LUA_TOP);
244     }
245 
246     /**
247     *   Use void to get a table
248     */  
249     public T get(T)(string varName)
250     {
251         checkLuaState();
252         if(lua_getglobal(L, (varName~'\0').ptr) != LUA_OK)
253         {
254             luaL_error(L, "Variable not existent");
255         }
256         static if(!is(T == void))
257             return getFromIndex!T(LUA_TOP);
258     }
259     public void getTable(string tableName){return get!void(tableName);}
260     public T getTableField(T)(string field, int ind)
261     {
262         assert(lua_istable(L, ind), "Index on lua stack is not a table");
263         assert(lua_getfield(L, ind, (field~'\0').ptr) == LUA_OK, "Could not get field named '"~field~"' from table");
264         return getFromIndex!T(field, LUA_TOP);
265     }
266 
267 
268     ~this(){if(L != null)lua_close(L);L = null;}
269 
270 
271     alias L this;
272 }